/*
 * Copyright (C) 2010-2011 Enrique Lara (enrique.posix2.lara@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
 * limitations under the License.
  */
package trs.wmata.api;

import app.timetogo.model.Agency;

import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.text.MessageFormat;
import java.util.Date;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

public final class Api {
    private static final int READ_TIMEOUT_MILLIS = 10000;
    private final Agency agency;
    private final String apiBase;
    private final String apiKey;

    public Api(final Agency agency, final String apiBase, final String apiKey) {
        this.agency = agency;
        this.apiBase = apiBase;
        this.apiKey = apiKey;
    }

    private static final String DATE_FORMAT = "yyyy-MM-dd";

    /** Returns train arrival information as it appears on the Public Information Displays throughout the system.  */
    private static final String API_NEXT_TRAIN = "{0}/StationPrediction.svc/GetPrediction/{2}?api_key={1}";

/** Returns a list of all bus routes. */
    private static final String API_BUS_ROUTES = "{0}/Bus.svc/Routes?api_key={1} ";

/** Returns the list of all bus stops.
 *  If a latitude/ longitude or radius is not provided or equals 0, all stops will be returned.
 *  Radius is expressed in meters.
 *  Stops are ordered by distance from latitude/ longitude */

//    private static final String API_BUS_STOPS_LAT_LONG = "{0}/Bus.svc/Stops?lat={2}&lon={3}&radius={4}&api_key={1}";

/** Returns the list of all bus stops.
 *  If a latitude/ longitude or radius is not provided or equals 0, all stops will be returned.
 *  Radius is expressed in meters.
 *  Stops are ordered by distance from latitude/ longitude, if provided. */
    private static final String API_BUS_STOPS_ALL = "{0}/Bus.svc/Stops?api_key={1}";

/** Returns the bus schedule for a specific bus stop. */
    private static final String API_BUS_STOP_SCHEDULE = "{0}/Bus.svc/StopSchedule?stopId={2}&date={3}&api_key={1}";

/** Returns the bus arrival predictions for a specific bus stop according to the real-time positions of the buses. */
    private static final String API_NEXT_BUS = "{0}/NextBusService.svc/Predictions?StopID={2}&api_key={1}";

    private static void sendRequest(final DefaultHandler handler, final String urlTemplate, Object... arguments) throws IOException, SAXException {
        if (System.getProperty("org.xml.sax.driver") == null) {
            System.setProperty("org.xml.sax.driver", "org.xmlpull.v1.sax2.Driver");
        }

        final String urlStr = MessageFormat.format(urlTemplate, arguments);
        final URL url = new URL(urlStr);
        final URLConnection c = url.openConnection();
        c.setReadTimeout(READ_TIMEOUT_MILLIS);

        final SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser;

        try {
            parser = factory.newSAXParser();
        } catch (final ParserConfigurationException e) {
            throw new RuntimeException(e); //XXX add Toast about being toast and return
        }

        final XMLReader xr = parser.getXMLReader();
        xr.setFeature("http://xml.org/sax/features/validation", false);
//        xr.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
//        xr.setProperty("http://xml.org/sax/properties/lexical-handler", handler);
        xr.setContentHandler(handler);
        xr.setErrorHandler(handler);
        xr.setDTDHandler(handler);
        xr.parse(new InputSource(c.getInputStream()));
    }

    public NextTrain getNextTrain(String codeStr, final String altCode) throws IOException, SAXException {
        if ((altCode != null) && (altCode.length() > 0)) {
            codeStr += ("," + altCode);
        }

        final NextTrain response = new NextTrain(agency);
        sendRequest(response, API_NEXT_TRAIN, apiBase, apiKey, codeStr);

        return response;
    }

    public BusRoutes getBusRoutes() throws IOException, SAXException {
        final BusRoutes response = new BusRoutes(agency);
        sendRequest(response, API_BUS_ROUTES, apiBase, apiKey);

        return response;
    }

    public BusStops getBusStops() throws IOException, SAXException {
        final BusStops response = new BusStops(agency);
        sendRequest(response, API_BUS_STOPS_ALL, apiBase, apiKey);

        return response;
    }

    public BusStopSchedule getBusStopSchedule(String stopId) throws IOException, SAXException {
        CharSequence dateStr = android.text.format.DateFormat.format(DATE_FORMAT, new Date());

        final BusStopSchedule response = new BusStopSchedule(agency);
        sendRequest(response, API_BUS_STOP_SCHEDULE, apiBase, apiKey, stopId, dateStr);

        return response;
    }

    public NextBus getNextBus(String stopId) throws IOException, SAXException {
        final NextBus response = new NextBus(agency);
        sendRequest(response, API_NEXT_BUS, apiBase, apiKey, stopId);

        return response;
    }
}
